package ru.kest.babymonitor.service.audio;

import android.util.Log;
import ru.kest.babymonitor.Settings;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;

/**
 * Class responsible for reading HTTP audio stream and sending audio package to AudioQueue for listener.
 */
public class AudioHTTPReader implements Runnable {

    private static String TAG = "IPCamBabyMonitorService";

    private static final int PACKAGE_BUF_SIZE = 550;
    private final static int CONNECTION_TIMEOUT = 4000;

    Settings settings = Settings.getInstance();
    AudioQueue audioQueue = AudioQueue.getInstance();

    private static FileOutputStream writer;
    private static DataOutputStream dos;

    @Override
    public void run() {
        try {
            URLConnection conn;
            int[] packet = new int[PACKAGE_BUF_SIZE];
            InputStream is = null;
            audioQueue.clear();

            try {
                conn = new URL(settings.getAudioUrl()).openConnection();
                conn.setConnectTimeout(CONNECTION_TIMEOUT);
                conn.setReadTimeout(CONNECTION_TIMEOUT);
                is = conn.getInputStream();

                Log.i(TAG, "Audio stream opened");
                int c, bt = -1, prev, count = 0;

                while (settings.isAudioServiceShouldWork() && !Thread.interrupted()) {
                    c = is.read();
                    if (c == -1) {
                        throw new RuntimeException("Read -1 from URL!!!");
//                        continue;
                    }
                    prev = bt;
                    bt = c;

                    if (prev == 0x55 && bt == 0xAA && count > 0) {
                        sendPackageToPlayer(packet, count);
                        count = 0;
                        packet = new int[PACKAGE_BUF_SIZE];
                    }

                    if (prev != -1) {
                        packet[count++] = prev;
                    }
                }
            } finally {
                if (is != null) is.close();
                if (dos != null) dos.close();
                Log.i(TAG, "AudioHTTPReader finished work");
            }
        } catch (Exception e) {
            Log.e(TAG, "AudioHTTPReader terminated! Needs to re-start it in a few seconds. " + e.getMessage(), e);
        }

    }

    private void sendPackageToPlayer(int[] buffer, int count) throws IOException {
        int[] cleanBuffer = getSubPacket(buffer, count);
        if (cleanBuffer != null) {
            short[] audioData = adpcmDecodeSoundPacketShort(cleanBuffer, settings.getSoundMultiplier());
            audioQueue.put(audioData);
            saveData(audioData);
//            Log.d(TAG, "Sent bytes: " + audioData.length);
        }
    }

    public static int[] getSubPacket(int[] buf, int count) {
        if (count <= 32) {
            Log.i(TAG, "Incorrect package size: " + count);
            return null;
        }
        return Arrays.copyOfRange(buf, 32, count);
    }

    private static JADPCMCodec codec = new JADPCMCodec();

    static short[] adpcmDecodeSoundPacketShort(int[] inputData, int multiplier) throws IOException {
        JADPCMCodec.JADPCMState jadpcmState = new JADPCMCodec.JADPCMState();
        short[] result = new short[inputData.length * 2];
        int count = 0;

        for (int anInputData : inputData) {
            int toDecode = anInputData & 0xFF;
            int firstSample = (toDecode & 0x0F);
            long decodedSample = codec.decode(jadpcmState, firstSample, multiplier);
            result[count++] = (short) decodedSample;
            int secondSample = (toDecode >> 4);
            decodedSample = codec.decode(jadpcmState, secondSample, multiplier);
            result[count++] = (short) decodedSample;
        }
        return result;
    }

    public void saveData(short[] data) throws IOException {
        if (dos != null) {
            for (short d : data) {
                dos.writeShort(d);
            }
        }
    };
}
